package com.theydream.qeechain.ARcaught;

import java.util.Enumeration;
import java.util.Vector;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class ARLayout extends View implements LocationListener,
SensorEventListener {

	private final float xAngleWidth = 29;
	private final float yAngleWidth = 19;
	public boolean clickok = true;

	public float screenWidth = 480;
	public float screenHeight = 320;

	volatile Vector<ARSphericalView> arViews = new Vector<ARSphericalView>();

	public SensorManager sensorMan;
	public LocationManager locMan;
	public Location curLocation = null;
	private Context ctx;
	public float direction = (float) 22.4;
	public double inclination;
	public double rollingX = (float) 0;
	public double rollingZ = (float) 0;
	public float kFilteringFactor = (float) 0.09;
	public float one = (float) 0;
	public float two = (float) 0;
	public float three = (float) 0;
	private boolean locationChanged = false;
	public boolean debug = false;
	public Drawable nextPet;

	public ARLayout(Context context) {
		super(context);
		ctx = context;

		sensorMan = (SensorManager) ctx
		.getSystemService(Context.SENSOR_SERVICE);
				sensorMan.registerListener(this,
						sensorMan.getDefaultSensor(Sensor.TYPE_ORIENTATION),
						SensorManager.SENSOR_DELAY_FASTEST);
				sensorMan.registerListener(this,
						sensorMan.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD),
						SensorManager.SENSOR_DELAY_FASTEST);
				sensorMan.registerListener(this,
						sensorMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
						SensorManager.SENSOR_DELAY_FASTEST);
		sensorMan.registerListener(this,
				sensorMan.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
				SensorManager.SENSOR_DELAY_FASTEST);
		locMan = (LocationManager) ctx
		.getSystemService(Context.LOCATION_SERVICE);
		locMan.requestLocationUpdates(LocationManager.GPS_PROVIDER, 100, 1,
				this);

	}

	public void onLocationChanged(Location location) {
		if (curLocation == null) {
			curLocation = location;
			ARSphericalView.deviceLocation = location;
			locationChanged = true;
		} else if (curLocation.getLatitude() == location.getLatitude()
				&& curLocation.getLongitude() == location.getLongitude())
			locationChanged = false;
		else
			locationChanged = true;

		curLocation = location;
		postInvalidate();
	}

	public void onProviderDisabled(String provider) {
	}

	public void onProviderEnabled(String provider) {
	}

	public void onStatusChanged(String provider, int status, Bundle extras) {
	}

	public void onAccuracyChanged(Sensor arg0, int arg1) {
	}

	//======================For Gyroscope================================
	private static final float NS2S = 1.0f / 1000000000.0f;
	private static final float EPSILON = 1.0f / 1000000000.0f;
	private final float[] deltaRotationVector = new float[4];
	private float timestamp;

	public void onSensorChanged(SensorEvent evt) {
		synchronized (this) {
			float vals[] = evt.values;
			float localDirection = 0;
			float[] matrixR = new float[9];
			float[] matrixI = new float[9];
			float[] matrixMagnetic=new float[3];
			float[] matrixAccelerometer=new float[3];

			//======================Using Gyroscope================================
			if (evt.sensor.getType()==Sensor.TYPE_GYROSCOPE)
			{
				// This timestep's delta rotation to be multiplied by the current rotation
				// after computing it from the gyro sample data.
				if (timestamp != 0) {
					final float dT = (evt.timestamp - timestamp) * NS2S;
					// Axis of the rotation sample, not normalized yet.
					rollingX = evt.values[0];
					direction = evt.values[1];
					rollingZ = evt.values[2];
					
					
					// Calculate the angular speed of the sample
					float omegaMagnitude = (float)Math.sqrt(rollingX*rollingX + direction*direction + rollingZ*rollingZ);

					// Normalize the rotation vector if it's big enough to get the axis
					if (omegaMagnitude > EPSILON) {
						rollingX /= omegaMagnitude;
						direction /= omegaMagnitude;
						rollingZ /= omegaMagnitude;
					}
					
					// Integrate around this axis with the angular speed by the timestep
					// in order to get a delta rotation from this sample over the timestep
					// We will convert this axis-angle representation of the delta rotation
					// into a quaternion before turning it into the rotation matrix.
					float thetaOverTwo = dT * omegaMagnitude / 2.0f;
					float sinThetaOverTwo = (float) Math.sin(thetaOverTwo);
					float cosThetaOverTwo = (float) Math.cos(thetaOverTwo);
					deltaRotationVector[0] = cosThetaOverTwo;
					deltaRotationVector[1] = sinThetaOverTwo * (float)rollingX;
					deltaRotationVector[2] = sinThetaOverTwo * direction;
					deltaRotationVector[3] = sinThetaOverTwo * (float)rollingZ;
					/* quaternion multiplication 
	              Reference: http://www.cprogramming.com/tutorial/3d/quaternions.html
					 */
					final float currentRotVector[] =  { 1, 0, 0, 0 };
					currentRotVector[0] = deltaRotationVector[0] * currentRotVector[0] - 
					deltaRotationVector[1] * currentRotVector[1] - 
					deltaRotationVector[2] * currentRotVector[2] - 
					deltaRotationVector[3] * currentRotVector[3];

					currentRotVector[1] = deltaRotationVector[0] * currentRotVector[1] + 
					deltaRotationVector[1] * currentRotVector[0] + 
					deltaRotationVector[2] * currentRotVector[3] - 
					deltaRotationVector[3] * currentRotVector[2];

					currentRotVector[2] = deltaRotationVector[0] * currentRotVector[2] - 
					deltaRotationVector[1] * currentRotVector[3] + 
					deltaRotationVector[2] * currentRotVector[0] + 
					deltaRotationVector[3] * currentRotVector[1];

					currentRotVector[3] = deltaRotationVector[0] * currentRotVector[3] + 
					deltaRotationVector[1] * currentRotVector[2] - 
					deltaRotationVector[2] * currentRotVector[1] + 
					deltaRotationVector[3] * currentRotVector[0];
					final float rad2deg = (float) (180.0f / Math.PI);
					float RotAngle = currentRotVector[0] * rad2deg;
					rollingX = currentRotVector[1];
					direction = currentRotVector[2];
					rollingZ = currentRotVector[3];
					Log.i("RRRR",Double.toString(RotAngle));
					Log.i("XXXXX",Double.toString(rollingX));
					Log.i("YYYYY",Double.toString(direction));
					Log.i("ZZZZZ",Double.toString(rollingZ));

					if (locationChanged)
						updateLayouts(localDirection, (float) inclination, curLocation);
					else
						updateLayouts(localDirection, (float) inclination, null);
				}
				timestamp = evt.timestamp;

			}
//			else if (evt.sensor.getType() == Sensor.TYPE_ORIENTATION)
//
//			{
//				float tmp = vals[0];
//
//				if (tmp > 10 && tmp < 350)
//					direction = (float) ((tmp * kFilteringFactor) + (direction * (1.0 - kFilteringFactor)));
//
//				else if (tmp >= 0 && tmp <= 10) {
//					direction = tmp;
//					// direction = (float) ((tmp * kFilteringFactor) + (direction *
//					// (1.0 - kFilteringFactor)));
//				} else if (tmp >= 350 && tmp <= 360) {
//					direction = tmp;
//					// direction = (float) ((tmp * kFilteringFactor) + (direction *
//					// (1.0 - kFilteringFactor)));
//				}
//
//				if (locationChanged)
//					updateLayouts(localDirection, (float) inclination, curLocation);
//				else
//					updateLayouts(localDirection, (float) inclination, null);
//
//			}

			//		if (evt.sensor.getType() == Sensor.TYPE_ACCELEROMETER) 
			//		{
			//			rollingZ = (vals[2] * kFilteringFactor)
			//					+ (rollingZ * (1.0 - kFilteringFactor));
			//			rollingX = (vals[0] * kFilteringFactor)
			//					+ (rollingX * (1.0 - kFilteringFactor));
			//		}
			
//			switch (evt.sensor.getType()) {
//            case Sensor.TYPE_ACCELEROMETER:
//                     System.arraycopy(evt.values, 0, matrixAccelerometer, 0, 3);
//                     break;
//            case Sensor.TYPE_MAGNETIC_FIELD:
//                     System.arraycopy(evt.values, 0, matrixMagnetic, 0, 3);
//             break;
//
//
//			}
//			
//			SensorManager.getRotationMatrix(
//					matrixR, matrixI,
//					matrixAccelerometer, matrixMagnetic);

//			if (rollingZ != 0.0) {
//				inclination = Math.atan2(rollingX ,rollingZ);// + Math.PI / 2.0;
//			} else if (rollingX < 0) {
//				inclination = Math.PI / 2.0;
//			} else if (rollingX >= 0) {
//				inclination = 3 * Math.PI / 2.0;
//			}
//
//			// convert to degress
//			inclination = inclination * (360 / (2 * Math.PI));
//
//			// flip!
//			if (inclination < 0)
//				inclination = inclination + 90;
//			else
//				inclination = inclination - 90;

//			inclination=SensorManager.getInclination(matrixI);
			
			Log.i("IIIIIIII",Double.toString(inclination));




			if (direction < 0)
				localDirection = 360 + direction;
			else
				localDirection = direction;

			if (locationChanged)
				updateLayouts(localDirection, (float) inclination, curLocation);
			else
				updateLayouts(localDirection, (float) inclination, null);
			postInvalidate();
		}
	}

	public void addARView(ARSphericalView view) {
		arViews.add(view);
	}

	// public void addAR1View(ImageView view){
	// arViews.add(view);
	// }
	public void removeARView(ARSphericalView view) {
		arViews.remove(view);
	}

	public void clearARViews() {
		arViews.removeAllElements();
	}

	private float calcXvalue(float leftArm, float rightArm, float az) {
		float offset;
		if (leftArm > rightArm) {
			if (az >= leftArm) {
				offset = az - leftArm;
			}
			if (az <= rightArm) {
				offset = 360 - leftArm + az;
			} else
				offset = az - leftArm;
		} else {
			offset = az - leftArm;
		}

		return (offset / xAngleWidth) * screenWidth;
	}

	private float calcYvalue(float lowerArm, float upperArm, float inc) {
		// distance in degress to the lower arm
		float offset = ((upperArm - yAngleWidth) - inc) * -1;
		return screenHeight - ((offset / yAngleWidth) * screenHeight);
	}

	public void onDraw(Canvas c) {
		// Log.e("Spec","Updating "+arViews.size()+" views");
		// long time = System.currentTimeMillis();
		Enumeration<ARSphericalView> e = arViews.elements();
		if (debug) {
			Paint p = new Paint();
			p.setColor(Color.WHITE);

			// c.drawText("Compass:" + String.valueOf(direction), 20, 20, p);

			// c.drawText("Inclination" + String.valueOf(inclination), 150, 20,
			// p);

		}

		while (e.hasMoreElements()) {
			ARSphericalView view = e.nextElement();
			view.draw(c,direction);

		}
	}

	public void updateLayouts(float Azi, float zAngle, Location l) {

		if (Azi != -1) {
			// Process the acceleromitor stuff
			float leftArm = Azi - (xAngleWidth / 2);
			float rightArm = Azi + (xAngleWidth / 2);
			if (leftArm < 0)
				leftArm = leftArm + 360;
			if (rightArm > 360)
				rightArm = rightArm - 360;

			float upperArm = zAngle + (yAngleWidth / 2);
			float lowerArm = zAngle - (yAngleWidth / 2);

			Enumeration<ARSphericalView> e = arViews.elements();

			if (arViews.size() == 0)
				return;

			while (e.hasMoreElements()) {
				// If we have a location, and the view has one, update it's data
				try {
					ARSphericalView view = e.nextElement();
					if (l != null && view.location != null) {
						view.azimuth = l.bearingTo(view.location);
						if (view.azimuth < 0)
							view.azimuth = 360 + view.azimuth;
						if (l.hasAltitude() && view.location.hasAltitude()) {
							view.inclination = (float) Math
							.atan(((view.location.getAltitude() - l
									.getAltitude()) / l
									.distanceTo(view.location)));
						}
					}
					// if(!isVisibleX(leftArm, rightArm, view.azimuth))
					// {
					// view.visible = false;
					// continue;
					// }
					// if(!isVisibleY(lowerArm, upperArm, view.inclination))
					// {
					// view.visible = false;
					// continue;
					// }
					view.visible = true;

					view.layout(
							(int) calcXvalue(leftArm, rightArm, view.azimuth),
							(int) calcYvalue(lowerArm, upperArm,
									view.inclination), view.getBottom(), view
									.getRight());
				} catch (Exception x) {
					Log.e("ArLayout", x.getMessage());
				}
			}

		}
		// 37.763557,-122.410719

	}

	public void close() {
		sensorMan.unregisterListener(this);
		locMan.removeUpdates(this);
	}

}
